/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.niir.goodfil.ui;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import org.apache.log4j.Logger;
import ru.niir.goodfil.db.Brand;
import ru.niir.goodfil.db.ClientGroup;
import ru.niir.goodfil.db.Good;
import ru.niir.goodfil.exceptions.CreateFailureException;
import ru.niir.goodfil.exceptions.GoodfilSecurityException;
import ru.niir.goodfil.exceptions.ValidationException;
import ru.niir.goodfil.ui.utils.SimpleMysqlBackuper;
import ru.niir.goodfil.utils.FloatPrice;
import ru.niir.goodfil.utils.PricelistItem;
import ru.niir.goodfil.utils.filters.GoodsFilter;

import javax.faces.model.SelectItem;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;

import static org.apache.commons.lang.StringUtils.isEmpty;

public class ImportPricelist2Bean extends PageBean {
    private final static Logger logger = Logger.getLogger(ImportPricelist2Bean.class);

    private static final int FORM_START = 1;
    private static final int FORM_BRANDS_AND_GOODS = 2;
    private static final int FORM_GOODS = 3;
    private static final int FORM_END = 4;

    private final static String MYSQL_USERNAME = "goodwill-store";
    private final static String MYSQL_PASSWORD = "6533xx";
    private final static String MYSQL_DB = "goodwill-store";

    private UUID sessionId;

    public ImportPricelist2Bean() {
        if (!getSecurityBean().getIsRoleAdministrator()) {
            throw new GoodfilSecurityException("Только администраторы имеют доступ к функции \"Импорт прейскуранта из Excel\"");
        }
    }

    private int formIndex = FORM_START;

    public boolean getIsStartForm() {
        return formIndex == FORM_START;
    }

    public boolean getIsBrandsAndGoodsForm() {
        return formIndex == FORM_BRANDS_AND_GOODS;
    }

    public boolean getIsGoodsForm() {
        return formIndex == FORM_GOODS;
    }

    public boolean getIsEndForm() {
        return formIndex == FORM_END;
    }

    public Brand getBrandFromCacheById(long brandId) {
        for (Brand brand : getBrands()) {
            if (brand.getId() == brandId) {
                return brand;
            }
        }
        return null;
    }

    public Brand getBrandFromCacheByName(String name) {
        for (Brand brand : getBrands()) {
            if (brand.getName().trim().toLowerCase().equals(name.trim().toLowerCase())) {
                return brand;
            }
        }
        return null;
    }

    public ClientGroup getClientGroupFromCacheByName(long brandId, String name) {
        for (ClientGroup clientGroup : getClientgroups()) {
            if (clientGroup.getName().trim().toLowerCase().equals(name.trim().toLowerCase()) && clientGroup.getBrandId() == brandId) {
                return clientGroup;
            }
        }
        return null;
    }

    public Good getGoodFromCacheById(long id) {
        for (Good good : getGoods()) {
            if (good.getId() == id) {
                return good;
            }
        }
        return null;
    }

    public Good getGoodFromCacheByCode(String code) {
        for (Good good : getGoods()) {
            if (good.getCode().equals(code)) {
                return good;
            }
        }
        return null;
    }

    public Good getGoodFromCacheBy1CCode(String code) {
        for (Good good : getGoods()) {
            if (good.getC1code().equals(code)) {
                return good;
            }
        }
        return null;
    }

    private List<Good> goods;
    private List<ClientGroupModel> clientgroupsModel;
    private List<ClientGroup> clientgroups;
    private List<Brand> brands;
    private Date timestamp = null;

    public void initTimestamp() {
        if (timestamp == null) {
            timestamp = Calendar.getInstance().getTime();
        }
    }

    public String getTimestampAsString() {
        initTimestamp();
        SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
        return sdf.format(timestamp);
    }

    public List<Good> getGoods() {
        if (goods == null) {
            GoodsFilter goodsFilter = new GoodsFilter();
            goodsFilter.setDeleted(0);
            goods = gf().getGoods(0, Integer.MAX_VALUE, goodsFilter);
        }
        return goods;
    }

    public List<ClientGroup> getClientgroups() {
        if (clientgroups == null) {
            clientgroups = gf().getClientGroups();
            for (ClientGroup clientGroup : clientgroups) {
                Brand brand = gf().getBrandById(clientGroup.getBrandId());
                clientGroup.setBrand(brand);
            }

            Collections.sort(clientgroups, new Comparator<ClientGroup>() {
                public int compare(ClientGroup cg1, ClientGroup cg2) {
                    if (cg1 == null || cg2 == null) {
                        return 0;
                    }

                    if (cg1.getBrandId() == cg2.getBrandId()) {
                        return cg1.getName().compareTo(cg2.getName());
                    } else {
                        return (int) (cg1.getBrandId() - cg2.getBrandId());
                    }
                }
            });

        }
        return clientgroups;
    }

    public List<ClientGroupModel> getClientgroupsModel() {
        if (clientgroupsModel == null) {
            clientgroupsModel = new ArrayList<ClientGroupModel>();
            for (ClientGroup cg : getClientgroups()) {
                ClientGroupModel clientGroupModel = new ClientGroupModel();
                Brand brand = getBrandFromCacheById(cg.getBrandId());
                clientGroupModel.setName(brand.getName() + " - " + cg.getName());
                clientgroupsModel.add(clientGroupModel);
            }
        }
        return clientgroupsModel;
    }

    public List<Brand> getBrands() {
        if (brands == null) {
            brands = gf().getBrands();
        }
        return brands;
    }

    public String getFilename() {
        gf().createIfNotExists(".import.pricelist.path", "c:\\Price.xml", "Путь к файлу с выгруженным прайсом");
        return gf().get(".import.pricelist.path");
    }

    public boolean getPriceIsAvailable() {
        String filename = getFilename();

        File f = new File(filename);
        return f.exists();
    }

    private Document document;

    private Set<String> missingBrands = new HashSet<String>();
    private Set<String> missingClientgroups = new HashSet<String>();

    private Set<NewGoodModel> newGoods = new HashSet<NewGoodModel>();
    private Set<ModifiedGoodModel> modifiedGoods = new HashSet<ModifiedGoodModel>();

    public Set<String> getMissingBrands() {
        return missingBrands;
    }

    public List<SelectItem> getMissingBrandsAsSelectItems() {
        List<SelectItem> result = new ArrayList<SelectItem>();
        for (String s : missingBrands) {
            result.add(new SelectItem(s, s));
        }
        return result;
    }

    public Set<String> getMissingClientgroups() {
        return missingClientgroups;
    }

    public List<SelectItem> getMissingClientgroupsAsSelectItems() {
        List<SelectItem> result = new ArrayList<SelectItem>();
        for (String s : missingClientgroups) {
            result.add(new SelectItem(s, s));
        }
        Collections.sort(result, new Comparator<SelectItem>() {
            public int compare(SelectItem s1, SelectItem s2) {
                if (s1 == null || s2 == null) return 0;
                return s1.getLabel().compareTo(s2.getLabel());
            }
        });
        return result;
    }

    public Set<NewGoodModel> getNewGoods() {
        return newGoods;
    }

    public List<NewGoodModel> getNewGoodsList() {
        return new ArrayList<NewGoodModel>(newGoods);
    }

    public Set<ModifiedGoodModel> getModifiedGoods() {
        return modifiedGoods;
    }

    public List<ModifiedGoodModel> getModifiedGoodsList() {
        return new ArrayList<ModifiedGoodModel>(modifiedGoods);
    }

    private final ProgressModel progress = new ProgressModel();

    public ProgressModel getProgress() {
        return progress;
    }

    /**
     * Загрузка Прайса из 1С, происходит вручную
     */
    public void loadPrice() {
        try {
            //  Бэкап
            this.sessionId = UUID.randomUUID();

            gf().createIfNotExists(".import.pricelist.backup.folder", "/home/sazonov/price_backups", "Путь к каталогу с бэкапами");
            String backupFolder = gf().get(".import.pricelist.backup.folder");
            String backupFilename = backupFolder + File.separator + sessionId;

//            SimpleMysqlBackuper smb = null;
//            try
//            {
//                smb = new SimpleMysqlBackuper(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_DB);
//                smb.backup(backupFilename);
//            }
//            catch (Exception e)
//            {
//                e.printStackTrace();
//                throw new BackupFailedException(e);
//            }

            XStream xs = new XStream();
            xs.processAnnotations(Document.class);
            xs.processAnnotations(Rest.class);

            document = (Document) xs.fromXML(new InputStreamReader(new FileInputStream(getFilename()), "windows-1251"));

            missingBrands.clear();
            missingClientgroups.clear();
            newGoods.clear();
            modifiedGoods.clear();

            //  Нормализация
            for (Rest rest : document.rests) {
                rest.brand = rest.brand.trim();
                rest.goodCode1C = rest.goodCode1C.trim();
                rest.goodCodeIS = rest.goodCodeIS
                        .replaceAll("  ", " ")
                        .trim()
                        .toUpperCase()
                        .replaceAll("А", "A")
                        .replaceAll("В", "B")
                        .replaceAll("Е", "E")
                        .replaceAll("К", "K")
                        .replaceAll("М", "M")
                        .replaceAll("Н", "H")
                        .replaceAll("О", "O")
                        .replaceAll("Р", "P")
                        .replaceAll("С", "C")
                        .replaceAll("Т", "T")
                        .replaceAll("У", "Y")
                        .replaceAll("Х", "X");

                if (isEmpty(rest.goodCodeIS)) {
                    continue;
                }

                Brand brand = getBrandFromCacheByName(rest.brand);
                if (brand == null) {
                    missingBrands.add(rest.brand);
                    continue;
                }

                String sClientGroup = rest.prices.split("=")[0].trim();
                if (getClientGroupFromCacheByName(brand.getId(), sClientGroup) == null) {
                    missingClientgroups.add(brand.getName() + " - " + sClientGroup);
                    continue;
                }

                Good good = getGoodFromCacheBy1CCode(rest.getGoodCode1C());
                if (good == null) {
                    //  Новый товар
                    NewGoodModel ng = new NewGoodModel();
                    ng.code1C = rest.goodCode1C;
                    ng.codeIS = rest.goodCodeIS;
                    ng.name = rest.goodNameIC;
                    newGoods.add(ng);
                } else {
                    //  Возможно измененный товар?
                    if (!good.getCode().equals(rest.getGoodCodeIS()) &&
                            !good.getName().equals(rest.getGoodNameIC())) {
                        //  Ну точно измененный
                        ModifiedGoodModel g = new ModifiedGoodModel();
                        g.code1C = rest.getGoodCode1C();
                        g.oldCodeIS = good.getCode();
                        g.oldName = good.getName();
                        g.newCodeIS = rest.getGoodCodeIS();
                        g.newName = rest.getGoodNameIC();
                        modifiedGoods.add(g);
                    }
                }
            }
            logger.debug("Form index is FORM_BRANDS_AND_GOODS");
            formIndex = FORM_BRANDS_AND_GOODS;
        } catch (IOException ioe) {
            ioe.printStackTrace();
            // TODO: не удалось прочитать файл
        }
    }

    public void checkGoods() {
        if (!goodsAndBrandsAgree) {
            facesError("Для продолжения необходимо согласиться с перечнем пропущенных брэндов и ценовых колонок");
            return;
        }

        formIndex = FORM_GOODS;
    }

    public void agreeAndContinue() {
        if (!goodsAgree) {
            facesError("Для продолжения необходимо согласиться с перечнем новых товаров и с переченем измененных товаров");
            return;
        }

        commit();
        formIndex = FORM_END;
    }

    public void cancel() {
        goodsAndBrandsAgree = false;
        goodsAgree = false;
        formIndex = FORM_START;
    }

    private final List<String> infoMsgs = new ArrayList<String>();
    private final List<String> errMsgs = new ArrayList<String>();

    public List<String> getInfoMsgs() {
        return infoMsgs;
    }

    public List<String> getErrMsgs() {
        return errMsgs;
    }

    public boolean getErrMsgsIsEmpty() {
        return errMsgs.size() == 0;
    }

    public void commit() {
        infoMsgs.clear();
        errMsgs.clear();

        progress.setStageName("Добавление новых товаров");
        List<NewGoodModel> newGoods = getNewGoodsList();
        for (int i = 0; i < newGoods.size(); i++) {
            progress.setProgress(String.valueOf(i + 1) + "/" + String.valueOf(newGoods.size() + 1));

            NewGoodModel gm = newGoods.get(i);
            Good g = new Good();
            g.setCode(gm.getCodeIS());
            g.setC1code(gm.getCode1C());
            g.setName(gm.getName());
            g.setGoodTypeId(1);
            g.setMinPrice(1);

            try {
                gf().saveGood(g);
                infoMsgs.add("Добавлен новый товар: " + g);
            } catch (ValidationException e) {
                errMsgs.add("Товар не добавлен (" + g + "): " + e);
                e.printStackTrace();
            } catch (CreateFailureException e) {
                errMsgs.add("Товар не добавлен (" + g + "): " + e);
                e.printStackTrace();
            }
        }

        progress.setStageName("Изменение старых товаров");
        List<ModifiedGoodModel> modifiedGoods = getModifiedGoodsList();
        for (int i = 0; i < modifiedGoods.size(); i++) {
            progress.setProgress(String.valueOf(i + 1) + "/" + String.valueOf(modifiedGoods.size() + 1));

            ModifiedGoodModel gm = modifiedGoods.get(i);
            Good g = getGoodFromCacheBy1CCode(gm.getCode1C());
            g.setCode(gm.getNewCodeIS());
            g.setName(gm.getNewName());

            try {
                gf().updateGood(g);
                infoMsgs.add("Товар обновлен: " + g);
            } catch (ValidationException e) {
                errMsgs.add("Товар не обновлен (" + g + "): " + e);
                e.printStackTrace();
            }
        }

        goods = null;
        progress.setStageName("Очистка прайса");
        progress.setProgress("50");
        gf().clearPricelist();

        progress.setStageName("Загрузка прайса");
        for (int i = 0; i < document.getRests().size(); i++) {
            progress.setProgress(String.valueOf(i + 1) + "/" + String.valueOf(document.getRests().size() + 1));

            Rest rest = document.getRests().get(i);

            if (isEmpty(rest.goodCodeIS)) {
                continue;
            }

            Brand brand = getBrandFromCacheByName(rest.brand);
            if (brand == null) {
                continue;
            }

            String sClientGroup = rest.prices.split("=")[0].trim();
            String sPrice = rest.prices.split("=")[1].trim();
            FloatPrice floatPrice = new FloatPrice(0);
            floatPrice.setValue(new Float(sPrice));

            ClientGroup clientgroup = getClientGroupFromCacheByName(brand.getId(), sClientGroup);
            if (clientgroup == null) {
                continue;
            }

            Good good = getGoodFromCacheBy1CCode(rest.getGoodCode1C());
            if (good == null) {
                continue;
            }

            PricelistItem pricelistItem = new PricelistItem(good);
            pricelistItem.put(clientgroup.getId(), floatPrice);

            try {
                gf().updatePricelistItem(pricelistItem);
                infoMsgs.add("Цена на товар (" + good + ") и клиентскую группу (" + clientgroup + ") была установлена: " + String.valueOf(floatPrice));
            } catch (ValidationException e) {
                errMsgs.add("Цена на товар (" + good + ") и клиентскую группу (" + clientgroup + ") не установлена: " + e);
                e.printStackTrace();
            }
        }

        progress.setStageName("Загрузка прайса завершена");
        formIndex = FORM_END;
    }

    /**
     * Восстановление бэкапа
     */
    public void restoreBackup() {
        try {
            gf().createIfNotExists(".import.pricelist.backup.folder", "/home/sazonov/price_backups", "Путь к каталогу с бэкапами");
            String backupFolder = gf().get(".import.pricelist.backup.folder");
            String backupFilename = backupFolder + File.separator + sessionId;
            SimpleMysqlBackuper smb = new SimpleMysqlBackuper(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_DB);
            smb.restore(backupFilename);
        } catch (Exception e) {
            e.printStackTrace();
            facesError("НЕ УДАЛОСЬ ВОССТАНОВИТЬ СИСТЕМУ ИЗ БЭКАПА");
        }
    }

    public void restoreBackupAndReturn() {
        try {
            gf().createIfNotExists(".import.pricelist.backup.folder", "/home/sazonov/price_backups", "Путь к каталогу с бэкапами");
            String backupFolder = gf().get(".import.pricelist.backup.folder");
            String backupFilename = backupFolder + File.separator + sessionId;
            SimpleMysqlBackuper smb = new SimpleMysqlBackuper(MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_DB);
            smb.restore(backupFilename);

            cancel();
        } catch (Exception e) {
            e.printStackTrace();
            facesError("НЕ УДАЛОСЬ ВОССТАНОВИТЬ СИСТЕМУ ИЗ БЭКАПА");
        }
    }

    private boolean goodsAndBrandsAgree;
    private boolean goodsAgree;

    public boolean getGoodsAndBrandsAgree() {
        return goodsAndBrandsAgree;
    }

    public void setGoodsAndBrandsAgree(boolean goodsAndBrandsAgree) {
        this.goodsAndBrandsAgree = goodsAndBrandsAgree;
    }

    public boolean isGoodsAgree() {
        return goodsAgree;
    }

    public void setGoodsAgree(boolean goodsAgree) {
        this.goodsAgree = goodsAgree;
    }

    public static class ClientGroupModel {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static class NewGoodModel {
        private String codeIS;
        private String code1C;
        private String name;

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            NewGoodModel that = (NewGoodModel) o;

            if (code1C != null ? !code1C.equals(that.code1C) : that.code1C != null) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            return code1C != null ? code1C.hashCode() : 0;
        }

        public String getCodeIS() {
            return codeIS;
        }

        public void setCodeIS(String codeIS) {
            this.codeIS = codeIS;
        }

        public String getCode1C() {
            return code1C;
        }

        public void setCode1C(String code1C) {
            this.code1C = code1C;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static class ModifiedGoodModel {
        private String code1C;
        private String newCodeIS;
        private String oldCodeIS;
        private String newName;
        private String oldName;

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            ModifiedGoodModel that = (ModifiedGoodModel) o;

            if (code1C != null ? !code1C.equals(that.code1C) : that.code1C != null) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            return code1C != null ? code1C.hashCode() : 0;
        }

        public String getCode1C() {
            return code1C;
        }

        public void setCode1C(String code1C) {
            this.code1C = code1C;
        }

        public String getNewCodeIS() {
            return newCodeIS;
        }

        public void setNewCodeIS(String newCodeIS) {
            this.newCodeIS = newCodeIS;
        }

        public String getOldCodeIS() {
            return oldCodeIS;
        }

        public void setOldCodeIS(String oldCodeIS) {
            this.oldCodeIS = oldCodeIS;
        }

        public String getNewName() {
            return newName;
        }

        public void setNewName(String newName) {
            this.newName = newName;
        }

        public String getOldName() {
            return oldName;
        }

        public void setOldName(String oldName) {
            this.oldName = oldName;
        }
    }

    @XStreamAlias("Rest")
    public static class Rest {
        @XStreamAsAttribute
        private String goodCode1C;

        @XStreamAsAttribute
        private String goodNameIC;

        @XStreamAsAttribute
        private String goodCodeIS;

        @XStreamAsAttribute
        private String brand;

        @XStreamAsAttribute
        private String prices;

        public String getGoodCode1C() {

            return goodCode1C;
        }

        public void setGoodCode1C(String goodCode1C) {
            this.goodCode1C = goodCode1C;
        }

        public String getGoodNameIC() {
            return goodNameIC;
        }

        public void setGoodNameIC(String goodNameIC) {
            this.goodNameIC = goodNameIC;
        }

        public String getGoodCodeIS() {
            return goodCodeIS;
        }

        public void setGoodCodeIS(String goodCodeIS) {
            this.goodCodeIS = goodCodeIS;
        }

        public String getBrand() {
            return brand;
        }

        public void setBrand(String brand) {
            this.brand = brand;
        }

        public String getPrices() {
            return prices;
        }

        public void setPrices(String prices) {
            this.prices = prices;
        }
    }

    @XStreamAlias("Document")
    public static class Document {
        @XStreamAsAttribute
        private String type;

        @XStreamAlias("Rests")
        private List<Rest> rests;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public List<Rest> getRests() {
            return rests;
        }

        public void setRests(List<Rest> rests) {
            this.rests = rests;
        }
    }

    public static class ProgressModel {
        private String stageName;
        private String progress;

        public String getStageName() {
            return stageName;
        }

        public void setStageName(String stageName) {
            this.stageName = stageName;
        }

        public String getProgress() {
            return progress;
        }

        public void setProgress(String progress) {
            this.progress = progress;
        }
    }
}